﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows.Input;
#if TASKSUPPORT
using System.Threading.Tasks;
#endif
using System.Windows;
using MugenMvvmToolkit.Infrastructure;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Interfaces.Views;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.ViewModels;
using Expression = System.Linq.Expressions.Expression;

namespace MugenMvvmToolkit.Utils
{

#if NOHASHSET
    internal sealed class HashSet<T> : ICollection<T>
    {
    #region Fields

        private readonly Dictionary<T, bool> _dictionary;

    #endregion

    #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="HashSet{T}"/> class.
        /// </summary>
        public HashSet()
        {
            _dictionary = new Dictionary<T, bool>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HashSet{T}"/> class.
        /// </summary>
        public HashSet(IEnumerable<T> enumerable)
            : this()
        {
            Should.NotBeNull(enumerable, "enumerable");
            foreach (var item in enumerable)
            {
                Add(item);
            }
        }

    #endregion

    #region Implementation of IEnumerable

        /// <summary>
        ///     Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        ///     A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<T> GetEnumerator()
        {
            return _dictionary.Keys.GetEnumerator();
        }

        /// <summary>
        ///     Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        ///     An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

    #endregion

    #region Implementation of ICollection<T>

        /// <summary>
        ///     Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        /// <exception cref="T:System.NotSupportedException">
        ///     The <see cref="T:System.Collections.Generic.ICollection`1" /> is
        ///     read-only.
        /// </exception>
        public void Add(T item)
        {
            if (_dictionary.ContainsKey(item))
                return;
            _dictionary.Add(item, true);
        }

        /// <summary>
        ///     Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">
        ///     The <see cref="T:System.Collections.Generic.ICollection`1" /> is
        ///     read-only.
        /// </exception>
        public void Clear()
        {
            _dictionary.Clear();
        }

        /// <summary>
        ///     Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
        /// </summary>
        /// <returns>
        ///     true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />;
        ///     otherwise, false.
        /// </returns>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        public bool Contains(T item)
        {
            return _dictionary.ContainsKey(item);
        }

        /// <summary>
        ///     Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1" /> to an
        ///     <see cref="T:System.Array" />, starting at a particular <see cref="T:System.Array" /> index.
        /// </summary>
        /// <param name="array">
        ///     The one-dimensional <see cref="T:System.Array" /> that is the destination of the elements copied
        ///     from <see cref="T:System.Collections.Generic.ICollection`1" />. The <see cref="T:System.Array" /> must have
        ///     zero-based indexing.
        /// </param>
        /// <param name="arrayIndex">The zero-based index in <paramref name="array" /> at which copying begins.</param>        
        public void CopyTo(T[] array, int arrayIndex)
        {
            _dictionary.Keys.CopyTo(array, arrayIndex);
        }

        /// <summary>
        ///     Removes the first occurrence of a specific object from the
        ///     <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <returns>
        ///     true if <paramref name="item" /> was successfully removed from the
        ///     <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if
        ///     <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </returns>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        /// <exception cref="T:System.NotSupportedException">
        ///     The <see cref="T:System.Collections.Generic.ICollection`1" /> is
        ///     read-only.
        /// </exception>
        public bool Remove(T item)
        {
            return _dictionary.Remove(item);
        }

        /// <summary>
        ///     Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <returns>
        ///     The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </returns>
        public int Count
        {
            get { return _dictionary.Count; }
        }

        /// <summary>
        ///     Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
        /// </summary>
        /// <returns>
        ///     true if the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.
        /// </returns>
        public bool IsReadOnly
        {
            get { return false; }
        }

    #endregion
    }
#endif

    internal static partial class MvvmUtilsInternal
    {
#if HASEMIT
        #region Nested type: WeakEventCacheItem

        private sealed class WeakEventCacheItem : IEquatable<WeakEventCacheItem>
        {
            #region Equality members

            /// <summary>
            ///     Indicates whether the current object is equal to another object of the same type.
            /// </summary>
            /// <returns>
            ///     true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.
            /// </returns>
            /// <param name="other">An object to compare with this object.</param>
            public bool Equals(WeakEventCacheItem other)
            {
                return _delegateType == other._delegateType && Equals(_methodInfo, other._methodInfo) && _targetType == other._targetType;
            }

            /// <summary>
            ///     Indicates whether this instance and a specified object are equal.
            /// </summary>
            /// <returns>
            ///     true if <paramref name="obj" /> and this instance are the same type and represent the same value; otherwise, false.
            /// </returns>
            /// <param name="obj">Another object to compare to. </param>
            /// <filterpriority>2</filterpriority>
            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                return obj is WeakEventCacheItem && Equals((WeakEventCacheItem)obj);
            }

            /// <summary>
            ///     Returns the hash code for this instance.
            /// </summary>
            /// <returns>
            ///     A 32-bit signed integer that is the hash code for this instance.
            /// </returns>
            /// <filterpriority>2</filterpriority>
            public override int GetHashCode()
            {
                unchecked
                {
                    return ((_delegateType != null ? _delegateType.GetHashCode() : 0) * 397) ^
                           (_methodInfo != null ? _methodInfo.GetHashCode() : 0) ^
                           (_targetType != null ? _targetType.GetHashCode() : 0);
                }
            }

            #endregion

            #region Fields

            private readonly Type _delegateType;
            private readonly MethodInfo _methodInfo;
            private readonly Type _targetType;

            #endregion

            #region Constructor

            /// <summary>
            ///     Initializes a new instance of the <see cref="WeakEventCacheItem" /> class.
            /// </summary>
            public WeakEventCacheItem(Type delegateType, MethodInfo methodInfo, Type targetType)
            {
                _delegateType = delegateType;
                _methodInfo = methodInfo;
                _targetType = targetType;
            }

            #endregion
        }

        #endregion
#endif

        #region Fields

#if !NETFX_CORE
        internal static readonly HashSet<string> IgnoreAssembliesName = new HashSet<string>
        {
            "MSCORLIB",
            "SYSTEM.WINDOWS.RUNTIMEHOST",
            "SYSTEM.WINDOWS",
            "SYSTEM",
            "SYSTEM.NET",
            "SYSTEM.CORE",
            "SYSTEM.XML",
            "SYSTEM.RUNTIME.SERIALIZATION",
            "SYSTEM.WINDOWS.BROWSER",
            "SYSTEM.SERVICEMODEL.WEB",
            "SYSTEM.SERVICEMODEL.WEB",
            "MUGENMVVMTOOLKIT",
            "MUGENMVVMTOOLKIT.EXTRAS",
            "MUGENMVVMTOOLKIT.MUGENIOCADAPTER",
            "MUGENMVVMTOOLKIT.AUTOFACIOCADAPTER",
            "MUGENMVVMTOOLKIT.NINJECTIOCADAPTER",
            "MUGENINJECTION.CORE",
            "SYSTEM.COMPONENTMODEL.DATAANNOTATIONS",
            "SYSTEM.WINDOWS.CONTROLS.DATA.INPUT",
            "SYSTEM.WINDOWS.CONTROLS.DATA.INPUT",
            "SYSTEM.WINDOWS.CONTROLS",
            "SYSTEM.WINDOWS.CONTROLS.NAVIGATION",
            "SYSTEM.WINDOWS.DATA",
            "SYSTEM.WINDOWS.INTERACTIVITY",
            "SYSTEM.XML.LINQ",
            "SYSTEM.RUNTIME",
            "SYSTEM.SERVICEMODEL",
        };
#endif

        internal static readonly PropertyChangedEventArgs CountPropertyChangedArgs = new PropertyChangedEventArgs("Count");
        internal static readonly PropertyChangedEventArgs NotificationCountPropertyChangedArgs = new PropertyChangedEventArgs("NotificationCount");
        internal static readonly PropertyChangedEventArgs IndexerPropertyChangedArgs = new PropertyChangedEventArgs("Item[]");
        internal static readonly object[] Empty = new object[0];

        private static readonly Dictionary<Type, PropertyInfo> ViewToViewModelInterface = new Dictionary<Type, PropertyInfo>();
        private static readonly Dictionary<Type, PropertyInfo> ViewModelToViewInterface = new Dictionary<Type, PropertyInfo>();
        private static readonly Dictionary<Type, Func<object, ICommand>[]> TypesToCommandsProperties = new Dictionary<Type, Func<object, ICommand>[]>();

#if HASEMIT
        private static readonly Dictionary<WeakEventCacheItem, DynamicMethod> WeakDelegates =
            new Dictionary<WeakEventCacheItem, DynamicMethod>(30);
#endif

#if !NETFX_CORE
        private const BindingFlags PropertyBindingFlag =
            BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
#endif

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Object"/> class.
        /// </summary>
        static MvvmUtilsInternal()
        {

        }

        #endregion

        #region Methods

#if !HASEMIT
        /// <summary>
        ///     Returns a weak-reference version of a delegate.
        /// </summary>
        /// <param name="originalAction">The delegate to convert to weak referencing.</param>
        /// <param name="unsubcribeDelegate">The unregister action to invoke if the target is garbage collected.</param>
        /// <returns>A weak referencing delegate.</returns>
        internal static Delegate ConvertToWeakDelegate(Delegate originalAction, Action<Delegate> unsubcribeDelegate)
        {
            Should.NotBeNull(originalAction, "originalAction");
            if (originalAction.Target == null || GetWeakEventInfo(originalAction) != null)
                return originalAction;
            var weakEventInfo = new WeakEventInfo
            {
                Target = new WeakReference(originalAction.Target),
#if NETFX_CORE
                OriginalMethod = originalAction.GetMethodInfo()
#else
                OriginalMethod = originalAction.Method
#endif
            };
            Delegate result = ConvertToWeakDelegate(weakEventInfo, originalAction);
            if (unsubcribeDelegate != null)
                weakEventInfo.UnsubcribeDelegate = unsubcribeDelegate;
            if (unsubcribeDelegate != null)
                weakEventInfo.Delegate = result;
            return result;
        }

        /// <summary>
        /// Finds the weak-delegate use a original delegate in the specified array of delegate.
        /// </summary>
        /// <param name="originalAction">The original delegate to find weak delegate.</param>
        /// <param name="delegates">The specified array of delegate</param>
        /// <returns>An instance of delegate.</returns>
        internal static Delegate FindWeakDelegateByOriginalDelegate(Delegate originalAction, IList<Delegate> delegates)
        {
            Should.NotBeNull(originalAction, "originalAction");
            Should.NotBeNull(delegates, "delegates");
#if NETFX_CORE
            var methodInfo = originalAction.GetMethodInfo();
#else
            var methodInfo = originalAction.Method;
#endif
            foreach (var @delegate in delegates)
            {
                var weakEventInfo = GetWeakEventInfo(@delegate);
                if (weakEventInfo == null) continue;

                if (weakEventInfo.Target.Target == originalAction.Target &&
                    Equals(weakEventInfo.OriginalMethod, methodInfo))
                    return @delegate;
            }
            return null;
        }

        /// <summary>
        ///     Returns a weak-reference version of a delegate.
        /// </summary>
        internal static Delegate ConvertToWeakDelegate(WeakEventInfo weakEventInfo, Delegate method)
        {
            var methodInfo = weakEventInfo.OriginalMethod;
            Type targetType = method.Target.GetType();
            LabelTarget resultLabel = Expression.Label();
            ConstantExpression constantExpression = Expression.Constant(weakEventInfo);
            ParameterExpression resultVariable = Expression.Variable(methodInfo.ReturnType == typeof(void)
                                                                         ? typeof(object)
                                                                         : methodInfo.ReturnType);
            Expression.Assign(resultVariable, Expression.Default(resultVariable.Type));
            ParameterExpression localField = Expression.Variable(targetType);
            ParameterExpression[] methodCallParameters = methodInfo.GetParameters()
                                                                   .Select(
                                                                       info => Expression.Parameter(info.ParameterType))
                                                                   .ToArray();

            Expression methodCallExpression = Expression.Call(localField, methodInfo, methodCallParameters);
            if (methodInfo.ReturnType != typeof(void))
                methodCallExpression = Expression.Assign(resultVariable, methodCallExpression);
            MemberExpression weakReferenceField = Expression.Field(constantExpression, WeakEventInfo.TargetFieldInfo);

            BlockExpression body = Expression.Block
                (new[] { resultVariable },
                 Expression.IfThenElse(Expression.Equal(weakReferenceField, Expression.Constant(null)),
                                       Expression.Return(resultLabel),
                //if (weakEventInfo.Target == null)
                //   return null;
                //else
                                       Expression.Block
                                           (
                                               new[] { localField },
                                               Expression.Assign(localField,
                                                                 Expression.Convert(
                                                                     Expression.Property(weakReferenceField,
                                                                                         typeof(WeakReference)
                                                                                             .GetProperty("Target")),
                                                                     targetType)),
                //var target = weakEventInfo.OriginalDelegate.Target;
                //if (target == null){
                                               Expression.IfThenElse(
                                                   Expression.Equal(localField, Expression.Constant(null)),
                                                   Expression.Block
                                                       (
                                                           Expression.Call(constantExpression,
                                                                           WeakEventInfo.ClearMethodInfo),
                                                           Expression.Return(resultLabel)
                                                       ),
                //weakEventInfo.Clear();
                //return null;
                //}
                //else
                //return target.Invoke(arg1, arg2, .....);
                                                   Expression.Block(methodCallExpression, Expression.Return(resultLabel))))),
                 Expression.Label(resultLabel), resultVariable);


            return Expression.Lambda(method.GetType(), body, methodCallParameters).Compile();
        }

        private static WeakEventInfo GetWeakEventInfo(Delegate @delegate)
        {
            if (@delegate.Target == null)
                return null;
#if !NETFX_CORE
#if WINDOWS_PHONE
            if (!@delegate.Target.GetType().FullName.Equals("System.Runtime.CompilerServices.Closure"))
                return null;
            dynamic closure = @delegate.Target;
            IEnumerable<object> constants = closure.Constants;
#else
            var closure = @delegate.Target as Closure;
            if (closure == null)
                return null;
            IEnumerable<object> constants = closure.Constants;
#endif
#else
            if (!@delegate.Target.GetType().FullName.Equals("System.Runtime.CompilerServices.Closure"))
                return null;
            dynamic closure = @delegate.Target;
            IEnumerable<object> constants = closure.Constants;
#endif
            if (constants == null)
                return null;
            foreach (var constant in constants)
            {
                var weakEventInfo = constant as WeakEventInfo;
                if (weakEventInfo != null)
                    return weakEventInfo;
            }
            return null;
        }
#else
        /// <summary>
        ///     Returns a weak-reference version of a delegate.
        /// </summary>
        /// <param name="originalAction">The delegate to convert to weak referencing.</param>
        /// <param name="unsubcribeDelegate">The unregister action to invoke if the target is garbage collected.</param>
        /// <returns>A weak referencing delegate.</returns>
        internal static Delegate ConvertToWeakDelegate(Delegate originalAction, Action<Delegate> unsubcribeDelegate)
        {
            Should.NotBeNull(originalAction, "originalAction");
            if (originalAction.Target == null || originalAction.Target is WeakEventInfo)
                return originalAction;
            DynamicMethod weakMethodInternal = ConvertToWeakDelegate(originalAction);
            var weakEventInfo = new WeakEventInfo
                                    {
                                        Target = new WeakReference(originalAction.Target),
                                        OriginalMethod = originalAction.Method
                                    };
            if (unsubcribeDelegate != null)
                weakEventInfo.UnsubcribeDelegate = unsubcribeDelegate;

            Delegate weakDelegate = weakMethodInternal.CreateDelegate(originalAction.GetType(), weakEventInfo);
            if (unsubcribeDelegate != null)
                weakEventInfo.Delegate = weakDelegate;
            return weakDelegate;
        }

        /// <summary>
        /// Finds the weak-delegate use a original delegate in the specified array of delegate.
        /// </summary>
        /// <param name="originalAction">The original delegate to find weak delegate.</param>
        /// <param name="delegates">The specified array of delegate</param>
        /// <returns>An instance of delegate.</returns>
        internal static Delegate FindWeakDelegateByOriginalDelegate(Delegate originalAction, IList<Delegate> delegates)
        {
            Should.NotBeNull(originalAction, "originalAction");
            Should.NotBeNull(delegates, "delegates");
// ReSharper disable once LoopCanBeConvertedToQuery
            foreach (var @delegate in delegates)
            {
                var weakEventInfo = @delegate.Target as WeakEventInfo;
                if (weakEventInfo == null) continue;
                if (weakEventInfo.Target.Target == originalAction.Target && weakEventInfo.OriginalMethod == originalAction.Method)
                    return @delegate;
            }
            return null;
        }

        /// <summary>
        ///     Returns a weak-reference version of a delegate.
        /// </summary>
        /// <param name="originalAction">The delegate to convert to weak referencing.</param>
        /// <returns>A weak referencing delegate.</returns>
        internal static DynamicMethod ConvertToWeakDelegate(Delegate originalAction)
        {
#if !NETFX_CORE
            MethodInfo originalMethodDelegate = originalAction.Method;
#else
            MethodInfo originalMethodDelegate = originalAction.GetMethodInfo();
#endif

            Type delegateType = originalAction.GetType();
            var weakEventCacheItem = new WeakEventCacheItem(delegateType, originalMethodDelegate, originalAction.Target.GetType());

            using (DeadLockDetector.Lock(WeakDelegates))
            {
                DynamicMethod dynamicMethod;
                WeakDelegates.TryGetValue(weakEventCacheItem, out dynamicMethod);
                if (dynamicMethod == null)
                {
                    ParameterInfo[] parameterInfos = originalMethodDelegate.GetParameters();
                    var types = new Type[parameterInfos.Length + 1];
                    types[0] = typeof(WeakEventInfo);
                    for (int i = 0; i < parameterInfos.Length; i++)
                    {
                        types[i + 1] = parameterInfos[i].ParameterType;
                    }

                    dynamicMethod = CreateDynamicMethod(originalMethodDelegate.DeclaringType, types,
                                                        originalMethodDelegate.ReturnType);
                    ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                    Label originalDelegateIsNotNull = ilGenerator.DefineLabel();
                    Label returnNullLabel = ilGenerator.DefineLabel();
                    LocalBuilder declareLocal = ilGenerator.DeclareLocal(originalAction.Target.GetType());


                    for (Int16 i = 0; i < parameterInfos.Length; i++)
                    {
                        ParameterInfo parameterInfo = parameterInfos[i];
                        if (!parameterInfo.IsDefined(typeof(OutAttribute), true)) continue;
                        Type elementType = parameterInfo.ParameterType.GetElementType();
                        ilGenerator.Emit(OpCodes.Ldarg, i + 1);
                        ilGenerator.Emit(OpCodes.Initobj, elementType);
                    }

                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldfld, WeakEventInfo.TargetFieldInfo);
                    ilGenerator.Emit(OpCodes.Brfalse_S, returnNullLabel);
                    //if (weakEventInfo.Target == null)
                    //   return null;

                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldfld, WeakEventInfo.TargetFieldInfo);
#if NETFX_CORE
                    ilGenerator.Emit(OpCodes.Callvirt, typeof(WeakReference).GetProperty("Target").GetGetMethod(true));
#else
                    ilGenerator.Emit(OpCodes.Callvirt, typeof(WeakReference).GetProperty("Target").GetGetMethod());
#endif

                    ilGenerator.Emit(OpCodes.Castclass, declareLocal.LocalType);
                    ilGenerator.Emit(OpCodes.Stloc, declareLocal);
                    //var target = weakEventInfo.OriginalDelegate.Target;
                    ilGenerator.Emit(OpCodes.Ldloc_0);
                    ilGenerator.Emit(OpCodes.Brtrue_S, originalDelegateIsNotNull);
                    //if (target == null){

                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Callvirt, WeakEventInfo.ClearMethodInfo);
                    //weakEventInfo.Clear();
                    //}
                    ilGenerator.MarkLabel(returnNullLabel);
                    if (dynamicMethod.ReturnType != typeof(void))
                    {
                        if (dynamicMethod.ReturnType.IsValueType)
                        {
                            LocalBuilder result = ilGenerator.DeclareLocal(dynamicMethod.ReturnType);
                            ilGenerator.Emit(OpCodes.Ldloca_S, result);
                            ilGenerator.Emit(OpCodes.Initobj, dynamicMethod.ReturnType);
                            ilGenerator.Emit(OpCodes.Ldloc_S, result);
                        }
                        else
                            ilGenerator.Emit(OpCodes.Ldnull);
                    }
                    ilGenerator.Emit(OpCodes.Ret);
                    //return null;
                    //}

                    ilGenerator.MarkLabel(originalDelegateIsNotNull);
                    ilGenerator.Emit(OpCodes.Ldloc_0);
                    for (Int16 i = 1; i < types.Length; i++)
                    {
                        ilGenerator.Emit(OpCodes.Ldarg_S, i);
                    }
                    ilGenerator.Emit(OpCodes.Callvirt, originalMethodDelegate);
                    ilGenerator.Emit(OpCodes.Ret);
                    //return target.Invoke(arg1, arg2, .....);

                    WeakDelegates[weakEventCacheItem] = dynamicMethod;
                }
                return dynamicMethod;
            }
        }

        /// <summary>
        ///     Creates dynamic method with skip visibility.
        /// </summary>
        private static DynamicMethod CreateDynamicMethod(Type type, Type[] inputValue, Type outputValue)
        {
            return new DynamicMethod(
                                string.Format("_DynamicMethodMvvmToolkit_{0}_{1}", type.Name, Guid.NewGuid().ToString("N")),
                                outputValue,
                                inputValue, type.Module, true);
        }
#endif

        internal static bool CanInitializeView(IInitializationContext context)
        {
            var hasView = context.ViewModel as IHasView;
            return hasView != null && !context.ActivationPolicy.WithoutView
#if !WPF
 && !(hasView is NavigableViewModel)
#endif
;
        }

        internal static void DisposeCommands(object item)
        {
            Should.NotBeNull(item, "item");
            Func<object, ICommand>[] list;
            lock (TypesToCommandsProperties)
            {
                var type = item.GetType();
                if (!TypesToCommandsProperties.TryGetValue(type, out list))
                {
                    list = type
#if !NETFX_CORE
.GetProperties(PropertyBindingFlag)
#else
.GetProperties()
#endif
.Where(c => typeof(ICommand).IsAssignableFrom(c.PropertyType) && c.CanRead && c.GetIndexParameters().Length == 0)
.Select(CreatePropertyAccessor)
                        .ToArray();
                    TypesToCommandsProperties[type] = list;
                }
            }
            if (list.Length == 0) return;
            foreach (var func in list)
            {
#if !NETFX_CORE
                try
                {
#endif
                    var disposable = func(item) as IDisposable;
                    if (disposable == null) continue;
                    disposable.Dispose();
#if !NETFX_CORE
                }
                catch (MethodAccessException)
                {
                    //To avoid method access exception.                
                }
#endif
            }
        }

        internal static Func<object, ICommand> CreatePropertyAccessor(PropertyInfo property)
        {
            var target = Expression.Parameter(typeof(object), "instance");
            var accessExp = Expression.MakeMemberAccess(Expression.Convert(target, property.DeclaringType), property);
            return Expression.Lambda<Func<object, ICommand>>(accessExp, target).Compile();
        }

        internal static PropertyInfo GetViewModelProperty(Type viewType)
        {
            Should.BeView(viewType, "viewType");
            PropertyInfo result;
            using (DeadLockDetector.Lock(ViewToViewModelInterface))
            {
                if (!ViewToViewModelInterface.TryGetValue(viewType, out result))
                {
#if !NETFX_CORE
                    foreach (var @interface in viewType.GetInterfaces().Where(type => type.IsGenericType))
#else
                    foreach (var @interface in viewType.GetTypeInfo().ImplementedInterfaces.Where(type => type.GetTypeInfo().IsGenericType))
#endif

                    {
                        if (@interface.GetGenericTypeDefinition() != typeof(IView<>)) continue;
                        if (result != null)
                            Thrower.DuplicateInterface("view", "IView<>", viewType);
                        result = @interface.GetProperty("ViewModel");
                    }
                    ViewToViewModelInterface[viewType] = result;
                }
            }
            return result;
        }

        internal static PropertyInfo GetViewProperty(Type viewModelType)
        {
            Should.BeViewModel(viewModelType, "viewModelType");
            PropertyInfo result;
            using (DeadLockDetector.Lock(ViewModelToViewInterface))
            {
                if (!ViewModelToViewInterface.TryGetValue(viewModelType, out result))
                {
#if !NETFX_CORE
                    foreach (var @interface in viewModelType.GetInterfaces().Where(type => type.IsGenericType))
#else
                    foreach (var @interface in viewModelType.GetTypeInfo().ImplementedInterfaces.Where(type => type.GetTypeInfo().IsGenericType))
#endif

                    {
                        if (@interface.GetGenericTypeDefinition() != typeof(IHasView<>)) continue;
                        if (result != null)
                            Thrower.DuplicateInterface("view model", "IHasView<>", viewModelType);
                        result = @interface.GetProperty("View");
                    }
                    ViewModelToViewInterface[viewModelType] = result;
                }
            }
            return result;
        }

        /// <summary>
        ///     Gets the property name from specified <see cref="LambdaExpression" />.
        /// </summary>
        /// <param name="expression">
        ///     The specified <see cref="LambdaExpression" />.
        /// </param>
        /// <returns>The property name.</returns>
        internal static string ParsePropertyExpression(LambdaExpression expression)
        {
            Should.NotBeNull(expression, "expression");
            // Get the last element of the include path
            var unaryExpression = expression.Body as UnaryExpression;
            if (unaryExpression != null)
            {
                var memberExpression = unaryExpression.Operand as MemberExpression;
                if (memberExpression != null)
                    return memberExpression.Member.Name;
            }
            var expressionBody = expression.Body as MemberExpression;
            Should.BeSupported(expressionBody != null, "Expession {0} not supported", expression);
// ReSharper disable once PossibleNullReferenceException
            return expressionBody.Member.Name;
        }

        internal static void Invoke(this ExecutionType executionType, IThreadManager threadManager, Action action)
        {
            Should.NotBeNull(action, "action");
            switch (executionType)
            {
                case ExecutionType.None:
                    action();
                    break;
                case ExecutionType.SynchronousInUi:
                    Should.PropertyBeNotNull(threadManager, "ThreadManager");
                    threadManager.InvokeInUiSynchronous(action);
                    break;
                case ExecutionType.AsynchronousInUi:
                    Should.PropertyBeNotNull(threadManager, "ThreadManager");
                    threadManager.InvokeInUi(action);
                    break;
                case ExecutionType.Asynchronous:
                    Should.PropertyBeNotNull(threadManager, "ThreadManager");
                    threadManager.InvokeAsync(o => action());
                    break;
                default:
                    Thrower.EnumOutOfRange("executionType", executionType);
                    break;
            }
        }


#if !NETFX_CORE
        internal static IList<Assembly> GetAllAssemblies()
        {
#if SILVERLIGHT
            var listAssembly = new List<Assembly>();
            foreach (AssemblyPart part in Deployment.Current.Parts)
            {
#if WINDOWS_PHONE
                string assemblyName = part.Source.Replace(".dll", string.Empty);
                Assembly assembly = Assembly.Load(assemblyName);
#else
                var info = Application.GetResourceStream(new Uri(part.Source, UriKind.Relative));
                Assembly assembly = part.Load(info.Stream);
#endif
                listAssembly.Add(assembly);

            }
#else
            var listAssembly = AppDomain.CurrentDomain.GetAssemblies();
#endif
            return listAssembly.Where(assembly => !IgnoreAssembliesName.Contains(assembly.GetAssemblyName().ToUpper())).ToList();
        }
#endif

        internal static string GetAssemblyName(this Assembly assembly)
        {
            Should.NotBeNull(assembly, "assembly");
            return new AssemblyName(assembly.FullName).Name;
        }

        #endregion
    }
}